<?php

namespace WebmanGrpc\Pool;

use Grpc\Channel;
use Grpc\ChannelCredentials;
use WebmanGrpc\Grpc;

/**
 * 增强的连接池管理器
 * 
 * 提供连接池管理、健康检查、连接预热和泄漏检测功能。
 * 连接池可以显著提高gRPC客户端的性能，通过复用连接减少
 * 建立连接的开销，同时提供连接生命周期管理和监控。
 * 
 * 主要功能：
 * 1. 连接复用：复用已建立的连接，减少连接建立开销
 * 2. 健康检查：定期检查连接状态，移除不健康的连接
 * 3. 连接预热：预先建立连接，减少首次请求延迟
 * 4. 泄漏检测：检测和清理长时间未使用的连接
 * 5. 连接限制：限制连接池大小，防止资源耗尽
 * 6. 统计监控：提供连接使用统计和性能监控
 * 
 * 使用场景：
 * - 高并发gRPC调用场景
 * - 需要低延迟的微服务通信
 * - 资源受限的环境
 * - 需要连接监控和诊断的场景
 * 
 * 使用示例：
 * ```php
 * // 获取连接池实例
 * $pool = ConnectionPool::getInstance();
 * 
 * // 获取连接
 * $connection = $pool->getConnection('localhost:50051', [
 *     'timeout' => 5000,
 *     'credentials' => ChannelCredentials::createSsl()
 * ]);
 * 
 * // 使用连接进行gRPC调用
 * // ...
 * 
 * // 释放连接（实际上会返回到连接池）
 * $pool->releaseConnection('localhost:50051', $connection);
 * 
 * // 获取连接池统计信息
 * $stats = $pool->getStats();
 * echo "当前连接数: " . $stats['connections'] . "\n";
 * ```
 * 
 * @author Webman gRPC Team
 * @version 1.1.0
 * @since 1.0.0
 */
class ConnectionPool
{
    /**
     * 连接池实例
     * 
     * 单例模式的实例引用，确保整个应用中只有一个连接池实例。
     * 这种设计可以避免多个连接池实例之间的资源竞争和重复管理。
     * 
     * @var ?ConnectionPool
     */
    protected static ?ConnectionPool $instance = null;
    
    /**
     * 连接池
     * 
     * 存储所有活跃连接的数组，键为连接键（基于地址和选项生成），
     * 值为连接信息数组，包含通道对象、地址、选项、创建时间等信息。
     * 
     * 连接信息结构：
     * [
     *     'channel' => Channel,           // gRPC通道对象
     *     'address' => string,            // 服务地址
     *     'options' => array,             // 连接选项
     *     'created_at' => int,            // 创建时间戳
     *     'last_used' => int,             // 最后使用时间戳
     *     'last_health_check' => int,     // 最后健康检查时间戳
     *     'use_count' => int,             // 使用次数
     *     'is_healthy' => bool,           // 是否健康
     * ]
     * 
     * @var array
     */
    protected array $connections = [];
    
    /**
     * 连接配置
     * 
     * 连接池的配置参数，包括：
     * - max_connections: 最大连接数，默认10
     * - max_lifetime: 连接最大生命周期（秒），默认300
     * - idle_timeout: 空闲超时时间（秒），默认60
     * - health_check_interval: 健康检查间隔（秒），默认30
     * - leak_detection_interval: 泄漏检测间隔（秒），默认60
     * - leak_threshold: 泄漏检测阈值（秒），默认300
     * - warm_up_connections: 预热连接数，默认5
     * 
     * @var array
     */
    protected array $config = [];
    
    /**
     * 连接统计
     * 
     * 连接池的统计信息，用于监控和诊断：
     * - created: 已创建的连接数
     * - reused: 已复用的连接数
     * - destroyed: 已销毁的连接数
     * - health_checks: 已执行的健康检查数
     * - leaks_detected: 检测到的连接泄漏数
     * 
     * @var array
     */
    protected array $stats = [
        'created' => 0,
        'reused' => 0,
        'destroyed' => 0,
        'health_checks' => 0,
        'leaks_detected' => 0,
    ];
    
    /**
     * 最后清理时间
     * 
     * 记录最后一次执行连接清理操作的时间戳，用于限制清理频率，
     * 避免过于频繁的清理操作影响性能。
     * 
     * @var int
     */
    protected int $lastCleanupTime = 0;
    
    /**
     * 获取连接池实例
     * 
     * 使用单例模式获取连接池实例，确保整个应用中只有一个连接池实例。
     * 这种设计可以避免多个连接池实例之间的资源竞争和重复管理，
     * 同时简化了配置管理和统计信息收集。
     * 
     * 实现原理：
     * 1. 检查静态$instance属性是否为null
     * 2. 如果为null，创建新的ConnectionPool实例并赋值给$instance
     * 3. 返回$instance实例
     * 
     * 使用场景：
     * - 在应用启动时初始化连接池
     * - 在需要获取连接的地方获取连接池实例
     * - 在需要获取连接池统计信息的地方获取实例
     * 
     * 使用示例：
     * ```php
     * // 获取连接池实例
     * $pool = ConnectionPool::getInstance();
     * 
     * // 获取连接
     * $connection = $pool->getConnection('localhost:50051', $options);
     * ```
     * 
     * @return ConnectionPool 连接池实例
     */
    public static function getInstance(): ConnectionPool
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        
        return self::$instance;
    }
    
    /**
     * 构造函数
     * 
     * 初始化连接池，设置默认配置参数。构造函数被声明为private，
     * 确保只能通过getInstance()方法创建单例实例。
     * 
     * 初始化流程：
     * 1. 从Grpc类获取连接池配置
     * 2. 设置默认配置值
     * 3. 初始化统计信息
     * 
     * 默认配置：
     * - max_connections: 10 - 最大连接数
     * - max_lifetime: 300 - 连接最大生命周期（秒）
     * - idle_timeout: 60 - 空闲超时时间（秒）
     * - health_check_interval: 30 - 健康检查间隔（秒）
     * - leak_detection_interval: 60 - 泄漏检测间隔（秒）
     * - leak_threshold: 300 - 泄漏检测阈值（秒）
     * - warm_up_connections: 5 - 预热连接数
     * 
     * 使用场景：
     * - 在应用启动时自动初始化连接池
     * - 通过getInstance()方法间接调用
     */
    protected function __construct()
    {
        $this->config = Grpc::getConfig('client.connection_pool', []);
        
        // 启动健康检查定时器
        $this->startHealthCheckTimer();
        
        // 启动泄漏检测定时器
        $this->startLeakDetectionTimer();
    }
    
    /**
     * 获取连接
     * 
     * 从连接池中获取或创建一个连接。如果连接池中已有健康的连接，
     * 则复用现有连接；否则创建新连接。此方法还负责执行连接清理
     * 和健康检查，确保连接池中的连接都是可用的。
     * 
     * 获取连接流程：
     * 1. 生成连接键（基于地址和选项）
     * 2. 检查连接池中是否已有该键的连接
     * 3. 如果有，检查连接是否健康且未过期
     * 4. 如果连接可用，更新使用统计并返回
     * 5. 如果连接不可用或不存在，创建新连接
     * 6. 执行连接清理和健康检查
     * 7. 将新连接添加到连接池并返回
     * 
     * 连接键生成规则：
     * 连接键基于地址和选项的序列化字符串生成，确保相同地址和选项
     * 的请求会复用同一个连接。
     * 
     * 使用场景：
     * - 在gRPC客户端调用前获取连接
     * - 在连接预热过程中创建连接
     * - 在健康检查后重新创建不健康的连接
     * 
     * 使用示例：
     * ```php
     * // 获取连接池实例
     * $pool = ConnectionPool::getInstance();
     * 
     * // 获取连接
     * $connection = $pool->getConnection('localhost:50051', [
     *     'timeout' => 5000,
     *     'credentials' => ChannelCredentials::createSsl()
     * ]);
     * 
     * // 使用连接进行gRPC调用
     * // ...
     * ```
     * 
     * @param string $address gRPC服务器地址，格式为"host:port"
     * @param array $options 连接选项，包括超时、凭据等
     * @return Channel gRPC通道对象
     */
    public function getConnection(string $address, array $options = []): Channel
    {
        $key = $this->generateKey($address, $options);
        
        // 检查连接池中是否有可用连接
        if (isset($this->connections[$key])) {
            $connection = $this->connections[$key];
            
            // 检查连接是否有效且未过期
            if ($this->isConnectionValid($connection)) {
                $connection['last_used'] = time();
                $connection['use_count']++;
                $this->stats['reused']++;
                return $connection['channel'];
            }
            
            // 连接无效，移除
            $this->removeConnection($key);
        }
        
        // 创建新连接
        return $this->createConnection($key, $address, $options);
    }
    
    /**
     * 创建连接
     * 
     * 创建一个新的gRPC通道连接。此方法负责设置默认选项，
     * 处理凭证配置，并将连接添加到连接池中。
     * 
     * 创建流程：
     * 1. 设置凭证（如果未提供则使用不安全凭证）
     * 2. 使用gRPC扩展创建通道对象
     * 3. 将连接信息添加到连接池
     * 4. 更新创建统计信息
     * 5. 检查连接池大小限制
     * 6. 返回创建的通道对象
     * 
     * 凭证处理：
     * - 如果选项中提供了credentials，则使用提供的凭证
     * - 否则使用不安全凭证（仅用于开发环境）
     * - 生产环境应始终提供SSL/TLS凭证
     * 
     * 连接信息存储：
     * 每个连接在连接池中存储以下信息：
     * - channel: gRPC通道对象
     * - address: 服务地址
     * - options: 连接选项
     * - created_at: 创建时间戳
     * - last_used: 最后使用时间戳
     * - last_health_check: 最后健康检查时间戳
     * - use_count: 使用次数
     * - is_healthy: 健康状态
     * 
     * 使用场景：
     * - 在getConnection方法中创建新连接
     * - 在连接预热过程中创建连接
     * - 在健康检查失败后重新创建连接
     * 
     * 使用示例：
     * ```php
     * // 创建连接
     * $channel = $this->createConnection($key, 'localhost:50051', [
     *     'timeout' => 5000,
     *     'credentials' => ChannelCredentials::createSsl()
     * ]);
     * ```
     * 
     * @param string $key 连接键，用于在连接池中唯一标识连接
     * @param string $address gRPC服务器地址，格式为"host:port"
     * @param array $options 连接选项，包括超时、凭据等
     * @return Channel 创建的gRPC通道对象
     */
    protected function createConnection(string $key, string $address, array $options): Channel
    {
        // 设置凭证
        $credentials = null;
        if (isset($options['credentials']) && $options['credentials'] !== null) {
            $credentials = $options['credentials'];
        } else {
            $credentials = ChannelCredentials::createInsecure();
        }
        
        // 创建连接
        $channel = new Channel($address, $credentials, $options);
        
        // 添加到连接池
        $this->connections[$key] = [
            'channel' => $channel,
            'address' => $address,
            'options' => $options,
            'created_at' => time(),
            'last_used' => time(),
            'last_health_check' => 0,
            'use_count' => 1,
            'is_healthy' => true,
        ];
        
        // 更新统计
        $this->stats['created']++;
        
        // 检查连接池大小
        $this->checkPoolSize();
        
        return $channel;
    }
    
    /**
     * 连接预热
     * 
     * 预先创建连接以减少首次请求的延迟。预热连接可以在应用启动时
     * 或低峰期进行，确保在高并发时有足够的可用连接。
     * 
     * 预热流程：
     * 1. 获取最大预热连接数配置
     * 2. 遍历提供的服务地址列表
     * 3. 为每个地址生成连接键
     * 4. 检查连接是否已存在
     * 5. 如果不存在，创建新连接
     * 6. 记录预热连接数
     * 
     * 预热策略：
     * - 最多预热配置中指定的连接数
     * - 优先预热列表中靠前的地址
     * - 跳过已存在的连接，避免重复创建
     * 
     * 使用场景：
     * - 应用启动时预热常用服务的连接
     * - 在低峰期预热连接以应对即将到来的流量高峰
     * - 在服务发现后预热新发现的节点连接
     * 
     * 使用示例：
     * ```php
     * // 获取连接池实例
     * $pool = ConnectionPool::getInstance();
     * 
     * // 预热多个服务的连接
     * $pool->warmUp([
     *     'localhost:50051',
     *     'localhost:50052',
     *     'localhost:50053'
     * ], [
     *     'timeout' => 5000,
     *     'credentials' => ChannelCredentials::createSsl()
     * ]);
     * ```
     * 
     * @param array $addresses 需要预热的服务地址列表
     * @param array $options 连接选项，包括超时、凭据等
     * @return void
     */
    public function warmUp(array $addresses, array $options = []): void
    {
        $maxConnections = $this->config['warm_up_connections'] ?? 5;
        $count = 0;
        
        foreach ($addresses as $address) {
            if ($count >= $maxConnections) {
                break;
            }
            
            $key = $this->generateKey($address, $options);
            
            // 如果连接不存在，创建预热连接
            if (!isset($this->connections[$key])) {
                $this->createConnection($key, $address, $options);
                $count++;
            }
        }
    }
    
    /**
     * 检查连接是否有效
     * 
     * 检查连接是否满足使用条件，包括生命周期、空闲时间和健康状态。
     * 这是连接池管理的关键方法，确保只有有效的连接被复用。
     * 
     * 检查项目：
     * 1. 连接生命周期：检查连接是否超过最大生命周期
     * 2. 空闲时间：检查连接是否超过空闲超时时间
     * 3. 健康状态：检查连接是否标记为健康
     * 
     * 有效条件：
     * - 连接创建时间未超过最大生命周期（默认300秒）
     * - 连接最后使用时间未超过空闲超时时间（默认60秒）
     * - 连接健康状态为true
     * 
     * 使用场景：
     * - 在getConnection方法中检查现有连接是否可用
     * - 在连接清理过程中判断是否需要移除连接
     * - 在健康检查后更新连接状态
     * 
     * 使用示例：
     * ```php
     * // 检查连接是否有效
     * $isValid = $this->isConnectionValid($connection);
     * 
     * if ($isValid) {
     *     // 使用连接
     * } else {
     *     // 移除连接并创建新连接
     * }
     * ```
     * 
     * @param array $connection 连接信息数组，包含channel、address、options等
     * @return bool 连接是否有效
     */
    protected function isConnectionValid(array $connection): bool
    {
        $now = time();
        $maxLifetime = $this->config['max_lifetime'] ?? 300;
        $idleTimeout = $this->config['idle_timeout'] ?? 60;
        
        // 检查连接是否过期
        if ($now - $connection['created_at'] > $maxLifetime) {
            return false;
        }
        
        // 检查连接是否空闲超时
        if ($now - $connection['last_used'] > $idleTimeout) {
            return false;
        }
        
        // 检查连接是否健康
        if (!$connection['is_healthy']) {
            return false;
        }
        
        return true;
    }
    
    /**
     * 移除连接
     * 
     * 从连接池中安全地移除连接，包括关闭通道和更新统计信息。
     * 这是连接生命周期管理的重要部分，确保资源得到正确释放。
     * 
     * 移除流程：
     * 1. 检查连接是否存在
     * 2. 关闭gRPC通道
     * 3. 从连接池中移除连接信息
     * 4. 更新销毁统计信息
     * 
     * 资源清理：
     * - 调用Channel::close()方法关闭gRPC通道
     * - 从connections数组中移除连接信息
     * - 更新stats中的destroyed计数器
     * 
     * 使用场景：
     * - 在连接过期后移除连接
     * - 在连接不健康时移除连接
     * - 在连接池大小超限时移除最少使用的连接
     * - 在应用关闭时清理所有连接
     * 
     * 使用示例：
     * ```php
     * // 移除特定连接
     * $this->removeConnection($connectionKey);
     * 
     * // 在清理循环中移除多个连接
     * foreach ($expiredKeys as $key) {
     *     $this->removeConnection($key);
     * }
     * ```
     * 
     * @param string $key 连接键，用于在连接池中唯一标识连接
     * @return void
     */
    protected function removeConnection(string $key): void
    {
        if (isset($this->connections[$key])) {
            $connection = $this->connections[$key];
            
            // 关闭连接
            if ($connection['channel'] instanceof Channel) {
                $connection['channel']->close();
            }
            
            // 从连接池中移除
            unset($this->connections[$key]);
            
            // 更新统计
            $this->stats['destroyed']++;
        }
    }
    
    /**
     * 检查连接池大小
     * 
     * 检查连接池中的连接数量是否超过最大限制，如果超过则移除
     * 最少使用的连接，确保连接池大小在合理范围内。
     * 
     * 检查和清理流程：
     * 1. 获取最大连接数配置
     * 2. 检查当前连接数是否超过限制
     * 3. 如果超过，按最后使用时间排序连接
     * 4. 计算需要移除的连接数量
     * 5. 移除最少使用的连接
     * 
     * 清理策略：
     * - 优先移除最后使用时间最早的连接
     * - 保留最近使用的连接，提高连接复用率
     * - 确保移除操作不会影响正在使用的连接
     * 
     * 使用场景：
     * - 在创建新连接后检查连接池大小
     * - 在连接预热后检查连接池大小
     * - 在定期维护任务中检查连接池大小
     * 
     * 使用示例：
     * ```php
     * // 在创建连接后检查连接池大小
     * $this->checkPoolSize();
     * 
     * // 在定期任务中检查连接池大小
     * $scheduler->add(function() {
     *     $this->checkPoolSize();
     * }, 60); // 每分钟检查一次
     * ```
     * 
     * @return void
     */
    protected function checkPoolSize(): void
    {
        $maxConnections = $this->config['max_connections'] ?? 10;
        
        if (count($this->connections) <= $maxConnections) {
            return;
        }
        
        // 按最后使用时间排序，移除最旧的连接
        uasort($this->connections, function ($a, $b) {
            return $a['last_used'] - $b['last_used'];
        });
        
        $toRemove = count($this->connections) - $maxConnections;
        $keys = array_keys($this->connections);
        
        for ($i = 0; $i < $toRemove; $i++) {
            $this->removeConnection($keys[$i]);
        }
    }
    
    /**
     * 启动健康检查定时器
     * 
     * 设置定期健康检查任务，自动检查连接池中所有连接的健康状态。
     * 健康检查可以及时发现并处理不健康的连接，提高系统稳定性。
     * 
     * 定时器设置：
     * - 使用Workerman的Timer组件设置定时任务
     * - 检查间隔由配置中的health_check_interval参数决定
     * - 默认检查间隔为30秒
     * 
     * 实现说明：
     * - 此方法应在连接池初始化时调用
     * - 定时任务会调用performHealthCheck方法执行实际检查
     * - 如果Workerman环境不可用，则忽略定时器设置
     * 
     * 使用场景：
     * - 在连接池初始化时启动健康检查
     * - 在配置更新后重新启动健康检查
     * - 在应用启动时设置定期维护任务
     * 
     * 使用示例：
     * ```php
     * // 在连接池初始化时启动健康检查
     * $pool = ConnectionPool::getInstance();
     * $pool->startHealthCheckTimer();
     * 
     * // 在应用启动时启动健康检查
     * Event::on('worker.start', function() {
     *     ConnectionPool::getInstance()->startHealthCheckTimer();
     * });
     * ```
     * 
     * @return void
     */
    protected function startHealthCheckTimer(): void
    {
        // 在实际环境中，这里应该使用定时任务或事件循环
        // 这里简化为定期调用
        $interval = $this->config['health_check_interval'] ?? 30;
        
        // 注册一个定时任务，定期执行健康检查
        // 这里只是示例，实际实现需要根据Webman的定时任务机制
        if (function_exists('\Workerman\Timer::add')) {
            \Workerman\Timer::add($interval, function () {
                $this->performHealthCheck();
            });
        }
    }
    
    /**
     * 执行健康检查
     * 
     * 遍历连接池中的所有连接，检查它们的健康状态，并更新连接信息。
     * 对于不健康的连接，会从连接池中移除，确保只有健康的连接被使用。
     * 
     * 健康检查流程：
     * 1. 获取当前时间和检查间隔配置
     * 2. 遍历所有连接
     * 3. 检查连接是否需要健康检查（基于时间间隔）
     * 4. 对需要检查的连接执行健康检查
     * 5. 更新连接的健康状态和检查时间
     * 6. 对于不健康的连接，从连接池中移除
     * 7. 更新健康检查统计信息
     * 
     * 检查策略：
     * - 基于时间间隔的检查，避免过于频繁的检查
     * - 只对超过检查间隔的连接进行检查
     * - 不健康的连接会被立即移除
     * 
     * 使用场景：
     * - 在定时任务中定期执行健康检查
     * - 在手动维护时检查连接状态
     * - 在监控系统中检查连接健康状态
     * 
     * 使用示例：
     * ```php
     * // 手动执行健康检查
     * $pool = ConnectionPool::getInstance();
     * $pool->performHealthCheck();
     * 
     * // 在定时任务中执行健康检查
     * $scheduler->add(function() {
     *     $pool = ConnectionPool::getInstance();
     *     $pool->performHealthCheck();
     * }, 30); // 每30秒执行一次
     * ```
     * 
     * @return void
     */
    public function performHealthCheck(): void
    {
        $now = time();
        $interval = $this->config['health_check_interval'] ?? 30;
        
        foreach ($this->connections as $key => $connection) {
            // 检查是否需要健康检查
            if ($now - $connection['last_health_check'] < $interval) {
                continue;
            }
            
            // 执行健康检查
            $isHealthy = $this->checkConnectionHealth($connection);
            
            // 更新健康状态
            $this->connections[$key]['is_healthy'] = $isHealthy;
            $this->connections[$key]['last_health_check'] = $now;
            
            // 如果连接不健康，标记为需要重新创建
            if (!$isHealthy) {
                $this->removeConnection($key);
            }
            
            // 更新统计
            $this->stats['health_checks']++;
        }
    }
    
    /**
     * 检查连接健康状态
     * 
     * 对单个连接执行健康检查，判断连接是否仍然可用。
     * 这是连接健康检查的核心方法，实现具体的检查逻辑。
     * 
     * 检查方法：
     * 1. 获取连接的通道对象
     * 2. 尝试获取连接状态
     * 3. 捕获可能的异常
     * 4. 根据检查结果返回健康状态
     * 
     * 检查策略：
     * - 简单实现：检查通道对象是否有效
     * - 高级实现：发送ping请求或调用健康检查方法
     * - 异常处理：捕获所有异常并标记为不健康
     * 
     * 实现说明：
     * 当前实现是一个简化版本，仅检查通道对象的有效性。
     * 在生产环境中，可能需要实现更复杂的健康检查逻辑，
     * 如发送特定的健康检查请求。
     * 
     * 使用场景：
     * - 在performHealthCheck方法中对每个连接执行检查
     * - 在获取连接前检查连接状态
     * - 在监控系统中检查特定连接的健康状态
     * 
     * 使用示例：
     * ```php
     * // 检查特定连接的健康状态
     * $isHealthy = $this->checkConnectionHealth($connection);
     * 
     * if ($isHealthy) {
     *     // 连接健康，可以使用
     * } else {
     *     // 连接不健康，需要移除
     * }
     * ```
     * 
     * @param array $connection 连接信息数组，包含channel、address、options等
     * @return bool 连接是否健康
     */
    protected function checkConnectionHealth(array $connection): bool
    {
        try {
            // 简单的健康检查：尝试获取连接状态
            // 在实际实现中，可能需要发送一个简单的ping请求
            $channel = $connection['channel'];
            
            // 这里只是一个示例，实际的健康检查可能需要更复杂的逻辑
            // 比如发送一个简单的gRPC请求来验证连接是否正常
            return true;
        } catch (\Throwable $e) {
            return false;
        }
    }
    
    /**
     * 启动泄漏检测定时器
     * 
     * 设置定期泄漏检测任务，自动检测长时间未使用的连接。
     * 连接泄漏检测可以防止连接资源被长时间占用，提高资源利用率。
     * 
     * 定时器设置：
     * - 使用Workerman的Timer组件设置定时任务
     * - 检测间隔由配置中的leak_detection_interval参数决定
     * - 默认检测间隔为60秒
     * 
     * 实现说明：
     * - 此方法应在连接池初始化时调用
     * - 定时任务会调用detectLeaks方法执行实际检测
     * - 如果Workerman环境不可用，则忽略定时器设置
     * 
     * 使用场景：
     * - 在连接池初始化时启动泄漏检测
     * - 在配置更新后重新启动泄漏检测
     * - 在应用启动时设置定期资源清理任务
     * 
     * 使用示例：
     * ```php
     * // 在连接池初始化时启动泄漏检测
     * $pool = ConnectionPool::getInstance();
     * $pool->startLeakDetectionTimer();
     * 
     * // 在应用启动时启动泄漏检测
     * Event::on('worker.start', function() {
     *     ConnectionPool::getInstance()->startLeakDetectionTimer();
     * });
     * ```
     * 
     * @return void
     */
    protected function startLeakDetectionTimer(): void
    {
        // 在实际环境中，这里应该使用定时任务或事件循环
        $interval = $this->config['leak_detection_interval'] ?? 60;
        
        // 注册一个定时任务，定期执行泄漏检测
        if (function_exists('\Workerman\Timer::add')) {
            \Workerman\Timer::add($interval, function () {
                $this->detectLeaks();
            });
        }
    }
    
    /**
     * 检测连接泄漏
     * 
     * 遍历连接池中的所有连接，检测长时间未使用的连接，
     * 并将这些连接视为泄漏连接进行清理。
     * 
     * 检测流程：
     * 1. 获取当前时间和泄漏阈值配置
     * 2. 遍历所有连接
     * 3. 检查连接最后使用时间是否超过阈值
     * 4. 对于超过阈值的连接，更新泄漏统计
     * 5. 移除泄漏的连接
     * 
     * 检测策略：
     * - 基于最后使用时间的检测
     * - 默认泄漏阈值为300秒（5分钟）
     * - 超过阈值未使用的连接被视为泄漏
     * - 泄漏连接会被立即移除
     * 
     * 使用场景：
     * - 在定时任务中定期执行泄漏检测
     * - 在手动维护时检查连接使用情况
     * - 在资源监控系统中检测连接泄漏
     * 
     * 使用示例：
     * ```php
     * // 手动执行泄漏检测
     * $pool = ConnectionPool::getInstance();
     * $pool->detectLeaks();
     * 
     * // 在定时任务中执行泄漏检测
     * $scheduler->add(function() {
     *     $pool = ConnectionPool::getInstance();
     *     $pool->detectLeaks();
     * }, 60); // 每分钟执行一次
     * ```
     * 
     * @return void
     */
    public function detectLeaks(): void
    {
        $now = time();
        $leakThreshold = $this->config['leak_threshold'] ?? 300; // 5分钟
        
        foreach ($this->connections as $key => $connection) {
            // 检查连接是否长时间未使用
            if ($now - $connection['last_used'] > $leakThreshold) {
                // 记录泄漏
                $this->stats['leaks_detected']++;
                
                // 移除泄漏的连接
                $this->removeConnection($key);
            }
        }
    }
    
    /**
     * 清理过期连接
     * 
     * 清理连接池中的无效连接，包括过期连接、空闲超时连接和不健康连接。
     * 此方法会限制清理频率，避免过于频繁的清理操作影响性能。
     * 
     * 清理流程：
     * 1. 获取当前时间
     * 2. 检查是否需要执行清理（基于时间间隔限制）
     * 3. 更新最后清理时间
     * 4. 遍历所有连接
     * 5. 检查连接是否有效
     * 6. 移除无效连接
     * 
     * 清理策略：
     * - 限制清理频率，默认最小间隔为10秒
     * - 使用isConnectionValid方法判断连接是否有效
     * - 清理所有无效连接，包括过期、空闲超时和不健康的连接
     * 
     * 使用场景：
     * - 在获取连接前执行清理
     * - 在定期维护任务中执行清理
     * - 在资源紧张时手动执行清理
     * 
     * 使用示例：
     * ```php
     * // 手动执行清理
     * $pool = ConnectionPool::getInstance();
     * $pool->cleanup();
     * 
     * // 在定期任务中执行清理
     * $scheduler->add(function() {
     *     $pool = ConnectionPool::getInstance();
     *     $pool->cleanup();
     * }, 30); // 每30秒执行一次
     * ```
     * 
     * @return void
     */
    public function cleanup(): void
    {
        $now = time();
        
        // 限制清理频率，避免频繁清理
        if ($now - $this->lastCleanupTime < 10) {
            return;
        }
        
        $this->lastCleanupTime = $now;
        
        // 移除无效连接
        foreach ($this->connections as $key => $connection) {
            if (!$this->isConnectionValid($connection)) {
                $this->removeConnection($key);
            }
        }
    }
    
    /**
     * 生成连接键
     * 
     * 基于服务地址和连接选项生成唯一的连接键，用于在连接池中
     * 标识和复用连接。相同的地址和选项会生成相同的键，确保
     * 连接复用的一致性。
     * 
     * 生成规则：
     * 1. 将服务地址和序列化的选项字符串连接
     * 2. 使用MD5哈希算法生成固定长度的键
     * 3. 返回生成的键字符串
     * 
     * 键的特性：
     * - 唯一性：相同地址和选项生成相同的键
     * - 固定长度：MD5哈希确保键长度固定
     * - 高效性：哈希算法计算速度快
     * - 冲突概率低：MD5哈希冲突概率极低
     * 
     * 使用场景：
     * - 在getConnection方法中生成连接键
     * - 在warmUp方法中检查连接是否已存在
     * - 在连接管理中标识特定连接
     * 
     * 使用示例：
     * ```php
     * // 生成连接键
     * $key = $this->generateKey('localhost:50051', [
     *     'timeout' => 5000,
     *     'credentials' => ChannelCredentials::createSsl()
     * ]);
     * 
     * // 检查连接是否存在
     * if (isset($this->connections[$key])) {
     *     // 复用连接
     * }
     * ```
     * 
     * @param string $address gRPC服务器地址，格式为"host:port"
     * @param array $options 连接选项数组，包括超时、凭据等
     * @return string 生成的连接键，用于在连接池中唯一标识连接
     */
    protected function generateKey(string $address, array $options): string
    {
        return md5($address . serialize($options));
    }
    
    /**
     * 获取连接池统计信息
     * 
     * 返回连接池的详细统计信息，包括当前连接数、各种操作计数
     * 和配置参数。这些信息可用于监控、调试和优化连接池性能。
     * 
     * 统计信息包括：
     * 1. connections: 当前连接池中的连接数量
     * 2. stats: 连接操作统计
     *    - created: 已创建的连接数
     *    - reused: 已复用的连接数
     *    - destroyed: 已销毁的连接数
     *    - health_checks: 已执行的健康检查数
     *    - leaks_detected: 检测到的连接泄漏数
     * 3. config: 连接池配置参数
     *    - max_connections: 最大连接数
     *    - max_lifetime: 连接最大生命周期
     *    - idle_timeout: 空闲超时时间
     *    - health_check_interval: 健康检查间隔
     *    - leak_detection_interval: 泄漏检测间隔
     *    - leak_threshold: 泄漏检测阈值
     *    - warm_up_connections: 预热连接数
     * 
     * 使用场景：
     * - 在监控系统中显示连接池状态
     * - 在调试时分析连接池使用情况
     * - 在性能优化时评估连接池效率
     * 
     * 使用示例：
     * ```php
     * // 获取连接池统计信息
     * $pool = ConnectionPool::getInstance();
     * $stats = $pool->getStats();
     * 
     * // 显示连接数
     * echo "当前连接数: " . $stats['connections'] . "\n";
     * 
     * // 显示复用率
     * $reuseRate = $stats['stats']['reused'] / 
     *               ($stats['stats']['created'] + $stats['stats']['reused']);
     * echo "连接复用率: " . ($reuseRate * 100) . "%\n";
     * ```
     * 
     * @return array 包含连接数、操作统计和配置的统计信息数组
     */
    public function getStats(): array
    {
        return [
            'connections' => count($this->connections),
            'stats' => $this->stats,
            'config' => $this->config,
        ];
    }
    
    /**
     * 关闭所有连接
     * 
     * 关闭连接池中的所有连接并清空连接池。此方法通常在应用关闭
     * 或重置连接池时调用，确保所有连接资源得到正确释放。
     * 
     * 关闭流程：
     * 1. 遍历所有连接
     * 2. 调用removeConnection方法关闭每个连接
     * 3. 清空连接池数组
     * 
     * 资源清理：
     * - 关闭所有gRPC通道
     * - 释放所有连接相关资源
     * - 重置连接池状态
     * 
     * 注意事项：
     * - 此方法会立即关闭所有连接，包括正在使用的连接
     * - 调用此方法后，需要重新创建连接才能使用
     * - 统计信息不会被重置，保留历史记录
     * 
     * 使用场景：
     * - 在应用关闭时清理资源
     * - 在连接池重置时清空所有连接
     * - 在测试环境中重置连接状态
     * 
     * 使用示例：
     * ```php
     * // 在应用关闭时关闭所有连接
     * register_shutdown_function(function() {
     *     $pool = ConnectionPool::getInstance();
     *     $pool->closeAll();
     * });
     * 
     * // 在重置连接池时关闭所有连接
     * $pool = ConnectionPool::getInstance();
     * $pool->closeAll();
     * 
     * // 重新初始化连接池
     * $pool->warmUp($addresses, $options);
     * ```
     * 
     * @return void
     */
    public function closeAll(): void
    {
        foreach ($this->connections as $key => $connection) {
            $this->removeConnection($key);
        }
        
        $this->connections = [];
    }
}